package com.sc.es.service.house;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.sc.es.common.*;
import com.sc.es.dto.HouseDetailDto;
import com.sc.es.dto.HouseDto;
import com.sc.es.dto.HousePictureDto;
import com.sc.es.entity.*;
import com.sc.es.form.DatatableSearch;
import com.sc.es.form.HouseForm;
import com.sc.es.form.MapSearch;
import com.sc.es.form.PhotoForm;
import com.sc.es.repo.*;
import com.sc.es.search.HouseIndexTemplate;
import com.sc.es.search.ISearchService;
import com.sc.es.service.IHouseService;
import com.sc.es.service.IQiNiuService;
import com.sc.es.util.LoginUserUtil;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.*;


/**
 * @author sc
 * Created on  2018/2/7
 */
@Service
public class HouseService implements IHouseService{

    @Value("${qiniu.cdn.prefix}")
    private String cdnPrefix;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private HouseRepo houseRepo;
    @Autowired
    private SubWayRepo subWayRepo;
    @Autowired
    private SubWayStationRepo subWayStationRepo;
    @Autowired
    private HousePictureRepo housePictureRepo;
    @Autowired
    private HouseDetailRepo houseDetailRepo;
    @Autowired
    private HouseTagRepo houseTagRepo;
    @Autowired
    private IQiNiuService  iQiNiuService;
    @Autowired
    private ISearchService searchService;


    @Override
    @Transactional
    public HouseResult save(HouseForm houseForm) {
        House house=modelMapper.map(houseForm,House.class);
        List<HousePictureDto> housePictureDtoList = new ArrayList<>();
        Date now = new Date();
        house.setCreateTime(now);
        house.setLastUpdateTime(now);
        house.setBathroom(1);
        house.setWatchTimes(0);
        house.setStatus(HouseStatus.NOT_AUDITED.getValue());
        house.setAdminId(LoginUserUtil.getLoginUserId());
        houseRepo.save(house);
        HouseDetail houseDetail=modelMapper.map(houseForm,HouseDetail.class);
        houseDetail.setHouseId(house.getId());
        houseDetail=wrapperSubWayInfo(houseForm,houseDetail);
        houseDetailRepo.save(houseDetail);
        if(!houseForm.getPhotos().isEmpty()){
            for (PhotoForm photoForm : houseForm.getPhotos()) {
                HousePicture housePicture = new HousePicture();
                modelMapper.map(photoForm,housePicture);
                housePicture.setHouseId(house.getId());
                housePicture.setCdnPrefix(cdnPrefix);
                housePictureRepo.save(housePicture);
                housePictureDtoList.add(modelMapper.map(housePicture,HousePictureDto.class));
            }
        }
        List<String> tags = houseForm.getTags();
        if(!tags.isEmpty()){
            for (String tag : tags) {
                HouseTag houseTag=new HouseTag();
                houseTag.setHouseId(house.getId());
                houseTag.setName(tag);
                houseTagRepo.save(houseTag);
            }
        }
        HouseDto houseDto = modelMapper.map(house,HouseDto.class);
        HouseDetailDto houseDetailDto = modelMapper.map(houseDetail,HouseDetailDto.class);
        houseDto.setHouseDetail(houseDetailDto);
        houseDto.setPictures(housePictureDtoList);
        houseDto.setCover(this.cdnPrefix+houseDto.getCover());
        houseDto.setTags(tags);
        houseDto.setSubscribeStatus(0);
        return new HouseResult(true,null,houseDto);
    }

    @Override
    public ServiceResult findAllHouse(DatatableSearch search) {
        List<HouseDto> houseDtoList = new ArrayList<>();

        Sort sort =new Sort(Sort.Direction.fromString(search.getDirection()),search.getOrderBy());
        int page = search.getStart()/search.getLength();
        Pageable pageable = new PageRequest(page,search.getLength(),sort);

        Specification<House> specification = (root, query, cb)->{
            Predicate predicate = cb.equal(root.get("adminId"),LoginUserUtil.getLoginUserId());
            predicate = cb.and(predicate,cb.notEqual(root.get("status"), HouseStatus.DELETED.getValue()));
            if(search.getCity()!=null){
                predicate = cb.and(predicate,cb.equal(root.get("cityEnName"), search.getCity()));
            }
            if(search.getStatus()!=null){
                predicate = cb.and(predicate,cb.equal(root.get("status"), search.getStatus()));
            }
            if(search.getCreateTimeMin()!=null){
                predicate = cb.and(predicate,cb.greaterThanOrEqualTo(root.get("createTime"), search.getCreateTimeMin()));
            }
            if(search.getCreateTimeMax()!=null){
                predicate = cb.and(predicate,cb.lessThanOrEqualTo(root.get("createTime"), search.getCreateTimeMax()));
            }
            if(search.getTitle()!=null){
                predicate = cb.and(predicate,cb.like(root.get("title"), "%"+search.getTitle()+"%"));
            }
            return predicate;
        };

        Page<House> all = houseRepo.findAll(specification,pageable);

        all.forEach(house->{
            HouseDto map = modelMapper.map(house, HouseDto.class);
            map.setCover(cdnPrefix+house.getCover());
            houseDtoList.add(map);
        });
        return new ServiceResult(all.getTotalElements(),houseDtoList);
    }

    @Override
    public HouseResult findCompleteOne(Integer id) {
        List<HousePictureDto> pictureDtos = new ArrayList<>();
        House house = houseRepo.findOne(id);
        if(house==null){
            return HouseResult.notFound();
        }
        HouseDetail detail = houseDetailRepo.findByHouseId(id);
        HouseDetailDto detailDto = modelMapper.map(detail, HouseDetailDto.class);

        List<HousePicture> pictures = housePictureRepo.findByHouseId(id);
        pictures.forEach(p->{
            HousePictureDto pictureDto = modelMapper.map(p, HousePictureDto.class);
            pictureDtos.add(pictureDto);
        });

        List<HouseTag> tagList = houseTagRepo.findAllByHouseId(id);
        List<String> tags = new ArrayList<>();
        tagList.forEach(t->tags.add(t.getName()));
        HouseDto houseDto = modelMapper.map(house, HouseDto.class);
        houseDto.setHouseDetail(detailDto);
        houseDto.setTags(tags);
        houseDto.setPictures(pictureDtos);
        return HouseResult.of(houseDto);
    }

    @Override
    @Transactional
    public HouseResult update(HouseForm houseForm) {
        House house = this.houseRepo.findOne(houseForm.getId());
        if (house == null) {
            return HouseResult.notFound();
        }

        HouseDetail detail = this.houseDetailRepo.findByHouseId(house.getId());
        if (detail == null) {
            return HouseResult.notFound();
        }

        HouseResult wrapperResult = wrapperDetailInfo(detail, houseForm);
        if (wrapperResult != null) {
            return wrapperResult;
        }

        houseDetailRepo.save(detail);

        List<HousePicture> pictures = generatePictures(houseForm, houseForm.getId());
        housePictureRepo.save(pictures);

        if (houseForm.getCover() == null) {
            houseForm.setCover(house.getCover());
        }

        modelMapper.map(houseForm, house);
        house.setLastUpdateTime(new Date());
        houseRepo.save(house);
        if(house.getStatus()==HouseStatus.PASSES.getValue()){
            searchService.index(house.getId());
        }
        return HouseResult.success();
    }

    @Override
    public HouseResult setCover(Integer targetId, Integer coverId) {
        HousePicture cover = housePictureRepo.findOne(coverId);
        if (cover == null) {
            return HouseResult.notFound();
        }

        houseRepo.updateCover(targetId, cover.getPath());
        return HouseResult.success();
    }

    @Override
    public HouseResult deletePhoto(Integer id) {
        HousePicture picture = housePictureRepo.findOne(id);
        if (picture == null) {
            return HouseResult.notFound();
        }
        try {
            Response response = this.iQiNiuService.deleteFile(picture.getPath());
            if (response.isOK()) {
                housePictureRepo.delete(id);
                return HouseResult.success();
            } else {
                return new HouseResult(false, response.error);
            }
        } catch (QiniuException e) {
            e.printStackTrace();
            return new HouseResult(false, e.getMessage());
        }
    }

    @Override
    public HouseResult changeStatusToPublish(Integer id,boolean ispb) {
        House one = houseRepo.findOne(id);
        if(one==null){
            return HouseResult.notFound();
        }
        if(ispb){
            one.setStatus(HouseStatus.PASSES.getValue());
        }else{
            one.setStatus(HouseStatus.NOT_AUDITED.getValue());
        }
        houseRepo.save(one);
        if(ispb){
            searchService.index(id);
        }else{
            searchService.remove(id);
        }
        return HouseResult.success();
    }

    @Override
    public HouseResult deleteHouse(Integer id) {
        House one = houseRepo.findOne(id);
        if(one==null){
            return HouseResult.notFound();
        }
        one.setStatus(HouseStatus.DELETED.getValue());
        houseRepo.save(one);
        searchService.remove(id);
        return HouseResult.success();
    }

    /**
     * 前端房源展示
     * @param form
     * @return
     */
    @Override
    public ServiceResult query(RentSearchForm form) {
        ServiceResult result = searchService.query(form);
        if(result.getTotal()==0){
            return new ServiceResult(new ArrayList<>());
        }
        //去数据库
        return new ServiceResult(wrapperHouseWithES(result.getResult()));
      //  return getDataFromMysql(form);
    }

    @Override
    public HouseResult findHouseInCity(String cityEnName) {
        List<House> houses = houseRepo.findByCityEnName(cityEnName);
        List<HouseDto> list = new ArrayList<>();
        houses.forEach(i-> {
            HouseDto houseDto = modelMapper.map(i, HouseDto.class);
            HouseDetail houseDetail = houseDetailRepo.findByHouseId(houseDto.getId());
            HouseDetailDto houseDetailDto = modelMapper.map(houseDetail, HouseDetailDto.class);
            houseDto.setHouseDetail(houseDetailDto);
            houseDto.setCover(this.cdnPrefix+houseDto.getCover());
            list.add(houseDto);
        });

        return HouseResult.of(list);
    }

    @Override
    public ServiceResult wholeMapQuery(MapSearch mapSearch) {
        ServiceResult result = searchService.mapQuery(mapSearch.getCityEnName(),
                mapSearch.getOrderBy(),mapSearch.getOrderDirection(),
                mapSearch.getStart(),mapSearch.getSize());
        if(result.getTotal()==0){
            return new ServiceResult(new ArrayList<>());
        }
        List ids = result.getResult();
        List<HouseDto> houseDtos = new ArrayList<>();
        List<String> tags = new ArrayList<>();
        ids.forEach(i->{
            House one = houseRepo.findOne((Integer)i);
            HouseDto houseDto = modelMapper.map(one, HouseDto.class);
            HouseDetail houseDetail = houseDetailRepo.findByHouseId(houseDto.getId());
            List<HouseTag> houseTags = houseTagRepo.findAllByHouseId(houseDto.getId());
            houseTags.forEach(k->tags.add(k.getName()));
            HouseDetailDto houseDetailDto = modelMapper.map(houseDetail, HouseDetailDto.class);
            houseDto.setHouseDetail(houseDetailDto);
            houseDto.setTags(tags);
            houseDto.setCover(this.cdnPrefix+houseDto.getCover());
            houseDtos.add(houseDto);
        });
        return new ServiceResult(result.getTotal(),houseDtos);
    }

    @Override
    public ServiceResult boundMapQuery(MapSearch mapSearch) {
        ServiceResult result = searchService.mapQuery(mapSearch);
        if(result.getTotal()==0){
            return new ServiceResult(new ArrayList<>());
        }
        List ids = result.getResult();
        List<HouseDto> houseDtos = new ArrayList<>();
        List<String> tags = new ArrayList<>();
        ids.forEach(i->{
            House one = houseRepo.findOne((Integer)i);
            HouseDto houseDto = modelMapper.map(one, HouseDto.class);
            HouseDetail houseDetail = houseDetailRepo.findByHouseId(houseDto.getId());
            List<HouseTag> houseTags = houseTagRepo.findAllByHouseId(houseDto.getId());
            houseTags.forEach(k->tags.add(k.getName()));
            HouseDetailDto houseDetailDto = modelMapper.map(houseDetail, HouseDetailDto.class);
            houseDto.setHouseDetail(houseDetailDto);
            houseDto.setTags(tags);
            houseDto.setCover(this.cdnPrefix+houseDto.getCover());
            houseDtos.add(houseDto);
        });
        return new ServiceResult(result.getTotal(),houseDtos);
    }


    /**
     * es中搜索索引
     * @param ids
     * @return
     */
    private List<HouseDto> wrapperHouseWithES(List<Integer> ids){
        List<HouseDto> res = new ArrayList<>();

        Map<Integer,HouseDto> id2Map = new HashMap<>();
        Iterable<House> all = houseRepo.findAll(ids);
        all.forEach(i->{
            HouseDto houseDto = modelMapper.map(i, HouseDto.class);
            List<HouseTag> allByHouseId = houseTagRepo.findAllByHouseId(i.getId());
            List<String> tags = new ArrayList<>();
            allByHouseId.forEach(t->tags.add(t.getName()));
            houseDto.setTags(tags);
            HouseDetail houseDetail = houseDetailRepo.findByHouseId(i.getId());
            houseDto.setHouseDetail(modelMapper.map(houseDetail,HouseDetailDto.class));
            res.add(houseDto);
            id2Map.put(i.getId(),houseDto);

        });
        //保证es查询和mysql查询顺序一致
        Collections.reverse(res);
        return res;
    }

    /**
     * 数据库查询数据
     * @param form
     * @return
     */
    private ServiceResult getDataFromMysql(RentSearchForm form){
        Sort sort = HouseSort.generateSort(form.getOrderBy(),form.getOrderDirection());
        int page = form.getStart();
        Pageable pageable = new PageRequest(page,form.getSize(),sort);

        Specification<House> specification = (root, query, cb) -> {
            Predicate predicate = cb.equal(root.get("status"),HouseStatus.PASSES.getValue());
            predicate = cb.and(predicate,cb.equal(root.get("cityEnName"),form.getCityEnName()));
            if(HouseSort.DISTANCE_TO_SUBWAY_KEY.equals(form.getOrderBy())){
                predicate = cb.and(predicate,cb.gt(root.get(HouseSort.DISTANCE_TO_SUBWAY_KEY),-1));
            }

            return predicate;
        };
        Page<House> all = houseRepo.findAll(specification, pageable);
        List<HouseDto> houseDtos = new ArrayList<>();
        all.forEach(i->{
            List<HouseTag> allByHouseId = houseTagRepo.findAllByHouseId(i.getId());
            List<String> tags = new ArrayList<>();
            allByHouseId.forEach(t->tags.add(t.getName()));
            HouseDetail houseDetail = houseDetailRepo.findByHouseId(i.getId());
            HouseDetailDto detailDto = modelMapper.map(houseDetail, HouseDetailDto.class);
            HouseDto map = modelMapper.map(i, HouseDto.class);
            map.setHouseDetail(detailDto);
            map.setTags(tags);
            houseDtos.add(map);
        });
        return new ServiceResult(houseDtos);
    }


    private HouseDetail wrapperSubWayInfo(HouseForm houseForm,HouseDetail houseDetail){
        if(houseForm.getSubwayLineId()!=null){
            Subway one = subWayRepo.findOne(houseForm.getSubwayLineId());
            houseDetail.setSubwayLineName(one.getName());
        }
        if(houseForm.getSubwayStationId()!=null){
            SubWayStation one = subWayStationRepo.findOne(houseForm.getSubwayStationId());
            houseDetail.setSubwayStationName(one.getName());
        }
        return houseDetail;
    }



    private HouseResult<HouseDto> wrapperDetailInfo(HouseDetail houseDetail, HouseForm houseForm) {
        Subway subway = subWayRepo.findOne(houseForm.getSubwayLineId());
        if (subway == null) {
            return new HouseResult<>(false, "Not valid subway line!");
        }

        SubWayStation subwayStation = subWayStationRepo.findOne(houseForm.getSubwayStationId());
        if (subwayStation == null || !subway.getId().equals(subwayStation.getSubwayId())) {
            return new HouseResult<>(false, "Not valid subway station!");
        }

        houseDetail.setSubwayLineId(subway.getId());
        houseDetail.setSubwayLineName(subway.getName());

        houseDetail.setSubwayStationId(subwayStation.getId());
        houseDetail.setSubwayStationName(subwayStation.getName());

        houseDetail.setDescription(houseForm.getDescription());
        houseDetail.setAddress(houseForm.getDetailAddress());
        houseDetail.setLayoutDesc(houseForm.getLayoutDesc());
        houseDetail.setRentWay(houseForm.getRentWay());
        houseDetail.setRoundService(houseForm.getRoundService());
        houseDetail.setTraffic(houseForm.getTraffic());
        return null;

    }



    private List<HousePicture> generatePictures(HouseForm form, Integer houseId) {
        List<HousePicture> pictures = new ArrayList<>();
        if (form.getPhotos() == null || form.getPhotos().isEmpty()) {
            return pictures;
        }

        for (PhotoForm photoForm : form.getPhotos()) {
            HousePicture picture = new HousePicture();
            picture.setHouseId(houseId);
            picture.setCdnPrefix(cdnPrefix);
            picture.setPath(photoForm.getPath());
            picture.setWidth(photoForm.getWidth());
            picture.setHeight(photoForm.getHeight());
            pictures.add(picture);
        }
        return pictures;
    }
}
